{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "<font color=\"red\">注</font>: 使用 tensorboard 可视化需要安装 tensorflow (TensorBoard依赖于tensorflow库，可以任意安装tensorflow的gpu/cpu版本)\n",
    "\n",
    "```shell\n",
    "pip install tensorflow-cpu\n",
    "```"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2024-04-30T03:08:26.466719400Z",
     "start_time": "2024-04-30T03:08:17.277867200Z"
    },
    "execution": {
     "iopub.execute_input": "2025-02-03T11:57:50.479112Z",
     "iopub.status.busy": "2025-02-03T11:57:50.478411Z",
     "iopub.status.idle": "2025-02-03T11:57:55.119372Z",
     "shell.execute_reply": "2025-02-03T11:57:55.118413Z",
     "shell.execute_reply.started": "2025-02-03T11:57:50.479041Z"
    },
    "tags": []
   },
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "/usr/local/lib/python3.10/site-packages/tqdm/auto.py:21: TqdmWarning: IProgress not found. Please update jupyter and ipywidgets. See https://ipywidgets.readthedocs.io/en/stable/user_install.html\n",
      "  from .autonotebook import tqdm as notebook_tqdm\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "sys.version_info(major=3, minor=10, micro=14, releaselevel='final', serial=0)\n",
      "matplotlib 3.10.0\n",
      "numpy 1.26.4\n",
      "pandas 2.2.3\n",
      "sklearn 1.6.0\n",
      "torch 2.5.1+cu124\n",
      "cuda:0\n"
     ]
    }
   ],
   "source": [
    "import matplotlib as mpl\n",
    "import matplotlib.pyplot as plt\n",
    "%matplotlib inline\n",
    "import numpy as np\n",
    "import sklearn\n",
    "import pandas as pd\n",
    "import os\n",
    "import sys\n",
    "import time\n",
    "from tqdm.auto import tqdm\n",
    "import torch\n",
    "import torch.nn as nn\n",
    "import torch.nn.functional as F\n",
    "\n",
    "print(sys.version_info)\n",
    "for module in mpl, np, pd, sklearn, torch:\n",
    "    print(module.__name__, module.__version__)\n",
    "    \n",
    "device = torch.device(\"cuda:0\") if torch.cuda.is_available() else torch.device(\"cpu\")\n",
    "print(device)\n",
    "\n",
    "seed = 42\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "jp-MarkdownHeadingCollapsed": true,
    "tags": []
   },
   "source": [
    "## 数据准备\n",
    "\n",
    "https://www.kaggle.com/competitions/cifar-10/data\n",
    "\n",
    "```shell\n",
    "$ tree -L 1 cifar-10                                    \n",
    "cifar-10\n",
    "├── sampleSubmission.csv\n",
    "├── test\n",
    "├── train\n",
    "└── trainLabels.csv\n",
    "```"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {
    "execution": {
     "iopub.execute_input": "2025-02-03T11:58:30.060527Z",
     "iopub.status.busy": "2025-02-03T11:58:30.059884Z",
     "iopub.status.idle": "2025-02-03T11:58:34.118891Z",
     "shell.execute_reply": "2025-02-03T11:58:34.117993Z",
     "shell.execute_reply.started": "2025-02-03T11:58:30.060481Z"
    },
    "tags": []
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "[(PosixPath('competitions/cifar-10/train/1.png'), 'frog'),\n",
      " (PosixPath('competitions/cifar-10/train/2.png'), 'truck'),\n",
      " (PosixPath('competitions/cifar-10/train/3.png'), 'truck'),\n",
      " (PosixPath('competitions/cifar-10/train/4.png'), 'deer'),\n",
      " (PosixPath('competitions/cifar-10/train/5.png'), 'automobile')]\n",
      "[(PosixPath('competitions/cifar-10/test/1.png'), 'cat'),\n",
      " (PosixPath('competitions/cifar-10/test/2.png'), 'cat'),\n",
      " (PosixPath('competitions/cifar-10/test/3.png'), 'cat'),\n",
      " (PosixPath('competitions/cifar-10/test/4.png'), 'cat'),\n",
      " (PosixPath('competitions/cifar-10/test/5.png'), 'cat')]\n",
      "50000 300000\n"
     ]
    }
   ],
   "source": [
    "from pathlib import Path\n",
    "\n",
    "DATA_DIR = Path(\".\")\n",
    "DATA_DIR1 =Path(\"competitions/cifar-10/\")\n",
    "train_lables_file = DATA_DIR / \"trainLabels.csv\"\n",
    "test_csv_file = DATA_DIR / \"sampleSubmission.csv\" #测试集模板csv文件\n",
    "train_folder = DATA_DIR1 / \"train\"\n",
    "test_folder = DATA_DIR1 / \"test\"\n",
    "\n",
    "#所有的类别\n",
    "class_names = [\n",
    "    'airplane',\n",
    "    'automobile',\n",
    "    'bird',\n",
    "    'cat',\n",
    "    'deer',\n",
    "    'dog',\n",
    "    'frog',\n",
    "    'horse',\n",
    "    'ship',\n",
    "    'truck',\n",
    "]\n",
    "\n",
    "def parse_csv_file(filepath, folder):\n",
    "    \"\"\"Parses csv files into (filename(path), label) format\"\"\"\n",
    "    results = []\n",
    "    #读取所有行\n",
    "    with open(filepath, 'r') as f:\n",
    "#         lines = f.readlines()  为什么加[1:]，可以试这个\n",
    "        #第一行不需要，因为第一行是标签\n",
    "        lines = f.readlines()[1:] \n",
    "    for line in lines:#依次去取每一行\n",
    "        image_id, label_str = line.strip('\\n').split(',')\n",
    "        image_full_path = folder / f\"{image_id}.png\"\n",
    "        results.append((image_full_path, label_str)) #得到对应图片的路径和分类\n",
    "    return results\n",
    "\n",
    "#解析对应的文件夹\n",
    "train_labels_info = parse_csv_file(train_lables_file, train_folder)\n",
    "test_csv_info = parse_csv_file(test_csv_file, test_folder)\n",
    "#打印\n",
    "import pprint\n",
    "pprint.pprint(train_labels_info[0:5])\n",
    "pprint.pprint(test_csv_info[0:5])\n",
    "print(len(train_labels_info), len(test_csv_info))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2024-04-30T03:09:30.583944400Z",
     "start_time": "2024-04-30T03:09:30.436028300Z"
    },
    "execution": {
     "iopub.execute_input": "2025-02-03T11:58:36.809420Z",
     "iopub.status.busy": "2025-02-03T11:58:36.808677Z",
     "iopub.status.idle": "2025-02-03T11:58:36.946506Z",
     "shell.execute_reply": "2025-02-03T11:58:36.945368Z",
     "shell.execute_reply.started": "2025-02-03T11:58:36.809360Z"
    },
    "tags": []
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "                            filepath       class\n",
      "0  competitions/cifar-10/train/1.png        frog\n",
      "1  competitions/cifar-10/train/2.png       truck\n",
      "2  competitions/cifar-10/train/3.png       truck\n",
      "3  competitions/cifar-10/train/4.png        deer\n",
      "4  competitions/cifar-10/train/5.png  automobile\n",
      "                                filepath       class\n",
      "0  competitions/cifar-10/train/45001.png       horse\n",
      "1  competitions/cifar-10/train/45002.png  automobile\n",
      "2  competitions/cifar-10/train/45003.png        deer\n",
      "3  competitions/cifar-10/train/45004.png  automobile\n",
      "4  competitions/cifar-10/train/45005.png    airplane\n",
      "                           filepath class\n",
      "0  competitions/cifar-10/test/1.png   cat\n",
      "1  competitions/cifar-10/test/2.png   cat\n",
      "2  competitions/cifar-10/test/3.png   cat\n",
      "3  competitions/cifar-10/test/4.png   cat\n",
      "4  competitions/cifar-10/test/5.png   cat\n"
     ]
    }
   ],
   "source": [
    "# train_df = pd.DataFrame(train_labels_info)\n",
    "train_df = pd.DataFrame(train_labels_info[0:45000])\n",
    "valid_df = pd.DataFrame(train_labels_info[45000:])\n",
    "test_df = pd.DataFrame(test_csv_info)\n",
    "\n",
    "train_df.columns = ['filepath', 'class']\n",
    "valid_df.columns = ['filepath', 'class']\n",
    "test_df.columns = ['filepath', 'class']\n",
    "\n",
    "print(train_df.head())\n",
    "print(valid_df.head())\n",
    "print(test_df.head())"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2024-04-30T03:09:40.361166200Z",
     "start_time": "2024-04-30T03:09:38.275234700Z"
    },
    "execution": {
     "iopub.execute_input": "2025-02-03T11:58:41.274145Z",
     "iopub.status.busy": "2025-02-03T11:58:41.273625Z",
     "iopub.status.idle": "2025-02-03T11:58:43.123868Z",
     "shell.execute_reply": "2025-02-03T11:58:43.122526Z",
     "shell.execute_reply.started": "2025-02-03T11:58:41.274113Z"
    },
    "tags": []
   },
   "outputs": [],
   "source": [
    "from PIL import Image\n",
    "from torch.utils.data import Dataset, DataLoader\n",
    "from torchvision import transforms\n",
    "\n",
    "class Cifar10Dataset(Dataset):\n",
    "    df_map = {\n",
    "        \"train\": train_df,\n",
    "        \"eval\": valid_df,\n",
    "        \"test\": test_df\n",
    "    }\n",
    "    label_to_idx = {label: idx for idx, label in enumerate(class_names)}\n",
    "    idx_to_label = {idx: label for idx, label in enumerate(class_names)}\n",
    "    def __init__(self, mode, transform=None):\n",
    "        self.df = self.df_map.get(mode, None)\n",
    "        if self.df is None:\n",
    "            raise ValueError(\"mode should be one of train, val, test, but got {}\".format(mode))\n",
    "\n",
    "        self.transform = transform\n",
    "        \n",
    "    def __getitem__(self, index):\n",
    "        img_path, label = self.df.iloc[index]\n",
    "        img = Image.open(img_path).convert('RGB')\n",
    "        # # img 转换为 channel first\n",
    "        # img = img.transpose((2, 0, 1))\n",
    "        # transform\n",
    "        img = self.transform(img)\n",
    "        # label 转换为 idx\n",
    "        label = self.label_to_idx[label]\n",
    "        return img, label\n",
    "    \n",
    "    def __len__(self):\n",
    "        return self.df.shape[0]\n",
    "    \n",
    "IMAGE_SIZE = 32\n",
    "mean, std = [0.4914, 0.4822, 0.4465], [0.247, 0.243, 0.261]\n",
    "\n",
    "transforms_train = transforms.Compose([\n",
    "        # resize\n",
    "        transforms.Resize((IMAGE_SIZE, IMAGE_SIZE)),\n",
    "        # random rotation 40\n",
    "        transforms.RandomRotation(40),\n",
    "        # horizaontal flip\n",
    "        transforms.RandomHorizontalFlip(),\n",
    "        transforms.ToTensor(),\n",
    "        transforms.Normalize(mean, std)\n",
    "    ])\n",
    "\n",
    "transforms_eval = transforms.Compose([\n",
    "        # resize\n",
    "        transforms.Resize((IMAGE_SIZE, IMAGE_SIZE)),\n",
    "        transforms.ToTensor(),\n",
    "        transforms.Normalize(mean, std)\n",
    "    ])\n",
    "\n",
    "train_ds = Cifar10Dataset(\"train\", transforms_train)\n",
    "eval_ds = Cifar10Dataset(\"eval\", transforms_eval) "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2024-04-30T03:09:43.545874100Z",
     "start_time": "2024-04-30T03:09:43.519888900Z"
    },
    "execution": {
     "iopub.execute_input": "2025-02-03T11:58:43.716924Z",
     "iopub.status.busy": "2025-02-03T11:58:43.715907Z",
     "iopub.status.idle": "2025-02-03T11:58:43.724397Z",
     "shell.execute_reply": "2025-02-03T11:58:43.722896Z",
     "shell.execute_reply.started": "2025-02-03T11:58:43.716840Z"
    },
    "tags": []
   },
   "outputs": [],
   "source": [
    "batch_size = 64\n",
    "train_dl = DataLoader(train_ds, batch_size=batch_size, shuffle=True, num_workers=4)   \n",
    "eval_dl = DataLoader(eval_ds, batch_size=batch_size, shuffle=False, num_workers=4)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "(tensor([-0.2207, -0.2280, -0.1984]), tensor([0.9980, 0.9955, 0.9041]))\n"
     ]
    }
   ],
   "source": [
    "# 遍历train_ds得到每张图片，计算每个通道的均值和方差\n",
    "# def cal_mean_std(ds):\n",
    "#     mean = 0.\n",
    "#     std = 0.\n",
    "#     for img, _ in ds:\n",
    "#         mean += img.mean(dim=(1, 2))\n",
    "#         std += img.std(dim=(1, 2))\n",
    "#     mean /= len(ds)\n",
    "#     std /= len(ds)\n",
    "#     return mean, std\n",
    "#\n",
    "# # 经过 normalize 后 均值为0，方差为1\n",
    "# print(cal_mean_std(train_ds))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 定义模型"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2024-04-30T03:33:59.691021200Z",
     "start_time": "2024-04-30T03:33:59.642042400Z"
    },
    "execution": {
     "iopub.execute_input": "2025-02-03T11:59:01.554957Z",
     "iopub.status.busy": "2025-02-03T11:59:01.554270Z",
     "iopub.status.idle": "2025-02-03T11:59:01.577144Z",
     "shell.execute_reply": "2025-02-03T11:59:01.576096Z",
     "shell.execute_reply.started": "2025-02-03T11:59:01.554898Z"
    },
    "tags": []
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "             model.0.weight             paramerters num: 864\n",
      "              model.0.bias              paramerters num: 32\n",
      "             model.2.weight             paramerters num: 9216\n",
      "              model.2.bias              paramerters num: 32\n",
      "             model.5.weight             paramerters num: 9216\n",
      "              model.5.bias              paramerters num: 32\n",
      "             model.7.weight             paramerters num: 9216\n",
      "              model.7.bias              paramerters num: 32\n",
      "            model.10.weight             paramerters num: 9216\n",
      "             model.10.bias              paramerters num: 32\n",
      "            model.12.weight             paramerters num: 9216\n",
      "             model.12.bias              paramerters num: 32\n",
      "            model.16.weight             paramerters num: 5120\n",
      "             model.16.bias              paramerters num: 10\n"
     ]
    }
   ],
   "source": [
    "class VGG(nn.Module):\n",
    "    def __init__(self, num_classes):\n",
    "        super().__init__()\n",
    "        self.model = nn.Sequential(\n",
    "            nn.Conv2d(in_channels=3, out_channels=32, kernel_size=3, padding=\"same\"),\n",
    "            nn.ReLU(),\n",
    "            nn.Conv2d(in_channels=32, out_channels=32, kernel_size=3, padding=\"same\"),\n",
    "            nn.ReLU(),\n",
    "            nn.MaxPool2d(kernel_size=2),\n",
    "            nn.Conv2d(in_channels=32, out_channels=32, kernel_size=3, padding=\"same\"),\n",
    "            nn.ReLU(),\n",
    "            nn.Conv2d(in_channels=32, out_channels=32, kernel_size=3, padding=\"same\"),\n",
    "            nn.ReLU(),\n",
    "            nn.MaxPool2d(kernel_size=2),\n",
    "            nn.Conv2d(in_channels=32, out_channels=32, kernel_size=3, padding=\"same\"),\n",
    "            nn.ReLU(),\n",
    "            nn.Conv2d(in_channels=32, out_channels=32, kernel_size=3, padding=\"same\"),\n",
    "            nn.ReLU(),\n",
    "            nn.MaxPool2d(kernel_size=2),\n",
    "            nn.Flatten(),\n",
    "            nn.Linear(512, num_classes),\n",
    "        )\n",
    "        self.init_weights()\n",
    "        \n",
    "    def init_weights(self):\n",
    "        \"\"\"使用 xavier 均匀分布来初始化全连接层、卷积层的权重 W\"\"\"\n",
    "        for m in self.modules():\n",
    "            if isinstance(m, (nn.Linear, nn.Conv2d)): # 全连接层、卷积层\n",
    "                nn.init.kaiming_uniform_(m.weight, mode='fan_in', nonlinearity='relu') # 采用kaiming_uniform初始化权重, mode='fan_in'表示权重初始化为均匀分布，nonlinearity='relu'表示激活函数为relu\n",
    "                if m.bias is not None:\n",
    "                    nn.init.constant_(m.bias, 0.01)  # 偏置初始化为常数值，比如0.01\n",
    "        \n",
    "    def forward(self, x):\n",
    "        return self.model(x)\n",
    "        \n",
    "for key, value in VGG(len(class_names)).named_parameters():\n",
    "    print(f\"{key:^40}paramerters num: {np.prod(value.shape)}\")\n",
    "    \n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 训练\n",
    "\n",
    "pytorch的训练需要自行实现，包括\n",
    "1. 定义损失函数\n",
    "2. 定义优化器\n",
    "3. 定义训练步\n",
    "4. 训练"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {
    "execution": {
     "iopub.execute_input": "2025-02-03T11:59:05.954586Z",
     "iopub.status.busy": "2025-02-03T11:59:05.953927Z",
     "iopub.status.idle": "2025-02-03T11:59:06.015382Z",
     "shell.execute_reply": "2025-02-03T11:59:06.014025Z",
     "shell.execute_reply.started": "2025-02-03T11:59:05.954542Z"
    },
    "tags": []
   },
   "outputs": [],
   "source": [
    "from sklearn.metrics import accuracy_score\n",
    "\n",
    "@torch.no_grad()\n",
    "def evaluating(model, dataloader, loss_fct):\n",
    "    loss_list = []\n",
    "    pred_list = []\n",
    "    label_list = []\n",
    "    for datas, labels in dataloader:\n",
    "        datas = datas.to(device)\n",
    "        labels = labels.to(device)\n",
    "        # 前向计算\n",
    "        logits = model(datas)\n",
    "        loss = loss_fct(logits, labels)         # 验证集损失\n",
    "        loss_list.append(loss.item())\n",
    "        \n",
    "        preds = logits.argmax(axis=-1)    # 验证集预测\n",
    "        pred_list.extend(preds.cpu().numpy().tolist())\n",
    "        label_list.extend(labels.cpu().numpy().tolist())\n",
    "        \n",
    "    acc = accuracy_score(label_list, pred_list)\n",
    "    return np.mean(loss_list), acc\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "jp-MarkdownHeadingCollapsed": true,
    "tags": []
   },
   "source": [
    "### TensorBoard 可视化\n",
    "\n",
    "\n",
    "训练过程中可以使用如下命令启动tensorboard服务。\n",
    "\n",
    "```shell\n",
    "tensorboard \\\n",
    "    --logdir=runs \\     # log 存放路径\n",
    "    --host 0.0.0.0 \\    # ip\n",
    "    --port 8848         # 端口\n",
    "```"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {
    "execution": {
     "iopub.execute_input": "2025-02-03T11:59:10.362921Z",
     "iopub.status.busy": "2025-02-03T11:59:10.361710Z",
     "iopub.status.idle": "2025-02-03T11:59:10.498764Z",
     "shell.execute_reply": "2025-02-03T11:59:10.497381Z",
     "shell.execute_reply.started": "2025-02-03T11:59:10.362860Z"
    },
    "tags": []
   },
   "outputs": [],
   "source": [
    "from torch.utils.tensorboard import SummaryWriter\n",
    "\n",
    "\n",
    "class TensorBoardCallback:\n",
    "    def __init__(self, log_dir, flush_secs=10):\n",
    "        \"\"\"\n",
    "        Args:\n",
    "            log_dir (str): dir to write log.\n",
    "            flush_secs (int, optional): write to dsk each flush_secs seconds. Defaults to 10.\n",
    "        \"\"\"\n",
    "        self.writer = SummaryWriter(log_dir=log_dir, flush_secs=flush_secs)\n",
    "\n",
    "    def draw_model(self, model, input_shape):\n",
    "        self.writer.add_graph(model, input_to_model=torch.randn(input_shape))\n",
    "        \n",
    "    def add_loss_scalars(self, step, loss, val_loss):\n",
    "        self.writer.add_scalars(\n",
    "            main_tag=\"training/loss\", \n",
    "            tag_scalar_dict={\"loss\": loss, \"val_loss\": val_loss},\n",
    "            global_step=step,\n",
    "            )\n",
    "        \n",
    "    def add_acc_scalars(self, step, acc, val_acc):\n",
    "        self.writer.add_scalars(\n",
    "            main_tag=\"training/accuracy\",\n",
    "            tag_scalar_dict={\"accuracy\": acc, \"val_accuracy\": val_acc},\n",
    "            global_step=step,\n",
    "        )\n",
    "        \n",
    "    def add_lr_scalars(self, step, learning_rate):\n",
    "        self.writer.add_scalars(\n",
    "            main_tag=\"training/learning_rate\",\n",
    "            tag_scalar_dict={\"learning_rate\": learning_rate},\n",
    "            global_step=step,\n",
    "            \n",
    "        )\n",
    "    \n",
    "    def __call__(self, step, **kwargs):\n",
    "        # add loss\n",
    "        loss = kwargs.pop(\"loss\", None)\n",
    "        val_loss = kwargs.pop(\"val_loss\", None)\n",
    "        if loss is not None and val_loss is not None:\n",
    "            self.add_loss_scalars(step, loss, val_loss)\n",
    "        # add acc\n",
    "        acc = kwargs.pop(\"acc\", None)\n",
    "        val_acc = kwargs.pop(\"val_acc\", None)\n",
    "        if acc is not None and val_acc is not None:\n",
    "            self.add_acc_scalars(step, acc, val_acc)\n",
    "        # add lr\n",
    "        learning_rate = kwargs.pop(\"lr\", None)\n",
    "        if learning_rate is not None:\n",
    "            self.add_lr_scalars(step, learning_rate)\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "jp-MarkdownHeadingCollapsed": true,
    "tags": []
   },
   "source": [
    "### Save Best\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {
    "execution": {
     "iopub.execute_input": "2025-02-03T11:59:13.158925Z",
     "iopub.status.busy": "2025-02-03T11:59:13.158049Z",
     "iopub.status.idle": "2025-02-03T11:59:13.168078Z",
     "shell.execute_reply": "2025-02-03T11:59:13.167109Z",
     "shell.execute_reply.started": "2025-02-03T11:59:13.158877Z"
    },
    "tags": []
   },
   "outputs": [],
   "source": [
    "class SaveCheckpointsCallback:\n",
    "    def __init__(self, save_dir, save_step=5000, save_best_only=True):\n",
    "        \"\"\"\n",
    "        Save checkpoints each save_epoch epoch. \n",
    "        We save checkpoint by epoch in this implementation.\n",
    "        Usually, training scripts with pytorch evaluating model and save checkpoint by step.\n",
    "\n",
    "        Args:\n",
    "            save_dir (str): dir to save checkpoint\n",
    "            save_epoch (int, optional): the frequency to save checkpoint. Defaults to 1.\n",
    "            save_best_only (bool, optional): If True, only save the best model or save each model at every epoch.\n",
    "        \"\"\"\n",
    "        self.save_dir = save_dir\n",
    "        self.save_step = save_step\n",
    "        self.save_best_only = save_best_only\n",
    "        self.best_metrics = -1\n",
    "        \n",
    "        # mkdir\n",
    "        if not os.path.exists(self.save_dir):\n",
    "            os.mkdir(self.save_dir)\n",
    "        \n",
    "    def __call__(self, step, state_dict, metric=None):\n",
    "        if step % self.save_step > 0:\n",
    "            return\n",
    "        \n",
    "        if self.save_best_only:\n",
    "            assert metric is not None\n",
    "            if metric >= self.best_metrics:\n",
    "                # save checkpoints\n",
    "                torch.save(state_dict, os.path.join(self.save_dir, \"best.ckpt\"))\n",
    "                # update best metrics\n",
    "                self.best_metrics = metric\n",
    "        else:\n",
    "            torch.save(state_dict, os.path.join(self.save_dir, f\"{step}.ckpt\"))\n",
    "\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "jp-MarkdownHeadingCollapsed": true,
    "tags": []
   },
   "source": [
    "### Early Stop"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "metadata": {
    "execution": {
     "iopub.execute_input": "2025-02-03T11:59:17.884479Z",
     "iopub.status.busy": "2025-02-03T11:59:17.883822Z",
     "iopub.status.idle": "2025-02-03T11:59:17.893866Z",
     "shell.execute_reply": "2025-02-03T11:59:17.892627Z",
     "shell.execute_reply.started": "2025-02-03T11:59:17.884429Z"
    },
    "tags": []
   },
   "outputs": [],
   "source": [
    "class EarlyStopCallback:\n",
    "    def __init__(self, patience=5, min_delta=0.01):\n",
    "        \"\"\"\n",
    "\n",
    "        Args:\n",
    "            patience (int, optional): Number of epochs with no improvement after which training will be stopped.. Defaults to 5.\n",
    "            min_delta (float, optional): Minimum change in the monitored quantity to qualify as an improvement, i.e. an absolute \n",
    "                change of less than min_delta, will count as no improvement. Defaults to 0.01.\n",
    "        \"\"\"\n",
    "        self.patience = patience\n",
    "        self.min_delta = min_delta\n",
    "        self.best_metric = -1\n",
    "        self.counter = 0\n",
    "        \n",
    "    def __call__(self, metric):\n",
    "        if metric >= self.best_metric + self.min_delta:\n",
    "            # update best metric\n",
    "            self.best_metric = metric\n",
    "            # reset counter \n",
    "            self.counter = 0\n",
    "        else: \n",
    "            self.counter += 1\n",
    "            \n",
    "    @property\n",
    "    def early_stop(self):\n",
    "        return self.counter >= self.patience\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## training"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "metadata": {
    "ExecutionIndicator": {
     "show": true
    },
    "execution": {
     "iopub.execute_input": "2025-02-03T12:01:18.006942Z",
     "iopub.status.busy": "2025-02-03T12:01:18.005927Z",
     "iopub.status.idle": "2025-02-03T12:04:21.629452Z",
     "shell.execute_reply": "2025-02-03T12:04:21.628335Z",
     "shell.execute_reply.started": "2025-02-03T12:01:18.006896Z"
    },
    "tags": []
   },
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "100%|██████████| 7040/7040 [03:03<00:00, 38.44it/s, epoch=9]\n"
     ]
    }
   ],
   "source": [
    "# 训练\n",
    "def training(\n",
    "    model, \n",
    "    train_loader, \n",
    "    val_loader, \n",
    "    epoch, \n",
    "    loss_fct, \n",
    "    optimizer, \n",
    "    tensorboard_callback=None,\n",
    "    save_ckpt_callback=None,\n",
    "    early_stop_callback=None,\n",
    "    eval_step=500,\n",
    "    ):\n",
    "    record_dict = {\n",
    "        \"train\": [],\n",
    "        \"val\": []\n",
    "    }\n",
    "    \n",
    "    global_step = 0\n",
    "    model.train()\n",
    "    with tqdm(total=epoch * len(train_loader)) as pbar:\n",
    "        for epoch_id in range(epoch):\n",
    "            # training\n",
    "            for datas, labels in train_loader:\n",
    "                datas = datas.to(device)\n",
    "                labels = labels.to(device)\n",
    "                # 梯度清空\n",
    "                optimizer.zero_grad()\n",
    "                # 模型前向计算\n",
    "                logits = model(datas)\n",
    "                # 计算损失\n",
    "                loss = loss_fct(logits, labels)\n",
    "                # 梯度回传\n",
    "                loss.backward()\n",
    "                # 调整优化器，包括学习率的变动等\n",
    "                optimizer.step()\n",
    "                preds = logits.argmax(axis=-1)\n",
    "            \n",
    "                acc = accuracy_score(labels.cpu().numpy(), preds.cpu().numpy())    \n",
    "                loss = loss.cpu().item()\n",
    "                # record\n",
    "                \n",
    "                record_dict[\"train\"].append({\n",
    "                    \"loss\": loss, \"acc\": acc, \"step\": global_step\n",
    "                })\n",
    "                \n",
    "                # evaluating\n",
    "                if global_step % eval_step == 0:\n",
    "                    model.eval()\n",
    "                    val_loss, val_acc = evaluating(model, val_loader, loss_fct)\n",
    "                    record_dict[\"val\"].append({\n",
    "                        \"loss\": val_loss, \"acc\": val_acc, \"step\": global_step\n",
    "                    })\n",
    "                    model.train()\n",
    "                    \n",
    "                    # 1. 使用 tensorboard 可视化\n",
    "                    if tensorboard_callback is not None:\n",
    "                        tensorboard_callback(\n",
    "                            global_step, \n",
    "                            loss=loss, val_loss=val_loss,\n",
    "                            acc=acc, val_acc=val_acc,\n",
    "                            lr=optimizer.param_groups[0][\"lr\"],\n",
    "                            )\n",
    "                \n",
    "                    # 2. 保存模型权重 save model checkpoint\n",
    "                    if save_ckpt_callback is not None:\n",
    "                        save_ckpt_callback(global_step, model.state_dict(), metric=val_acc)\n",
    "\n",
    "                    # 3. 早停 Early Stop\n",
    "                    if early_stop_callback is not None:\n",
    "                        early_stop_callback(val_acc)\n",
    "                        if early_stop_callback.early_stop:\n",
    "                            print(f\"Early stop at epoch {epoch_id} / global_step {global_step}\")\n",
    "                            return record_dict\n",
    "                    \n",
    "                # udate step\n",
    "                global_step += 1\n",
    "                pbar.update(1)\n",
    "                pbar.set_postfix({\"epoch\": epoch_id})\n",
    "        \n",
    "    return record_dict\n",
    "        \n",
    "\n",
    "epoch = 10\n",
    "\n",
    "model = VGG(num_classes=10)\n",
    "\n",
    "# 1. 定义损失函数 采用交叉熵损失\n",
    "loss_fct = nn.CrossEntropyLoss()\n",
    "# 2. 定义优化器 采用 adam\n",
    "# Optimizers specified in the torch.optim package,可以修改beta1,beta2,weight_decay等参数\n",
    "optimizer = torch.optim.Adam(model.parameters(), lr=0.001,betas=(0.9,0.999))\n",
    "\n",
    "# 1. tensorboard 可视化\n",
    "if not os.path.exists(\"runs\"):\n",
    "    os.mkdir(\"runs\")\n",
    "tensorboard_callback = TensorBoardCallback(\"runs/vgg\")\n",
    "tensorboard_callback.draw_model(model, [1, 3, IMAGE_SIZE, IMAGE_SIZE])\n",
    "# 2. save best\n",
    "if not os.path.exists(\"checkpoints\"):\n",
    "    os.makedirs(\"checkpoints\")\n",
    "save_ckpt_callback = SaveCheckpointsCallback(\"checkpoints/vgg\", save_step=len(train_dl), save_best_only=True)\n",
    "# 3. early stop\n",
    "early_stop_callback = EarlyStopCallback(patience=5)\n",
    "\n",
    "model = model.to(device)\n",
    "record = training(\n",
    "    model, \n",
    "    train_dl, \n",
    "    eval_dl, \n",
    "    epoch, \n",
    "    loss_fct, \n",
    "    optimizer, \n",
    "    tensorboard_callback=tensorboard_callback,\n",
    "    save_ckpt_callback=save_ckpt_callback,\n",
    "    early_stop_callback=early_stop_callback,\n",
    "    eval_step=len(train_dl)\n",
    "    )"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "metadata": {
    "execution": {
     "iopub.execute_input": "2025-02-03T12:04:46.652296Z",
     "iopub.status.busy": "2025-02-03T12:04:46.651745Z",
     "iopub.status.idle": "2025-02-03T12:04:47.154143Z",
     "shell.execute_reply": "2025-02-03T12:04:47.153089Z",
     "shell.execute_reply.started": "2025-02-03T12:04:46.652254Z"
    },
    "tags": []
   },
   "outputs": [
    {
     "data": {
      "image/png": "iVBORw0KGgoAAAANSUhEUgAAAzwAAAHACAYAAABnOW2lAAAAOnRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjEwLjAsIGh0dHBzOi8vbWF0cGxvdGxpYi5vcmcvlHJYcgAAAAlwSFlzAAAPYQAAD2EBqD+naQAA7tBJREFUeJzsnXd4W4X1v9+ract7xCOJs/eGJEDChgxIoexSoGW0pVAIpc2vKx2UUZoWKIVvyygUCqVQKBRoCyEkBELIgISEQEjibMcZ3tuyrXl/f1xdSbYlW7LlqfM+jx9bV3ccybr2/dxzzucoqqqqCIIgCIIgCIIgDEIMfR2AIAiCIAiCIAhCTyGCRxAEQRAEQRCEQYsIHkEQBEEQBEEQBi0ieARBEARBEARBGLSI4BEEQRAEQRAEYdAigkcQBEEQBEEQhEGLCB5BEARBEARBEAYtIngEQRAEQRAEQRi0mPo6gEjwer2cOHGClJQUFEXp63AEQRDiBlVVaWhoYOjQoRgMco9MR/4vCYIg9B3R/m8aEILnxIkTFBQU9HUYgiAIccvRo0cZPnx4X4fRb5D/S4IgCH1PpP+bBoTgSUlJAbQXlZqaGvX2LpeL1atXs2jRIsxmc6zD6xEGYswgcfc2AzHugRgzxG/c9fX1FBQU+P8OCxrx+H8JJO7eZiDGPRBjBom7t+nt/00DQvDo5QKpqald/sdis9lITU0dMB+GgRgzSNy9zUCMeyDGDBK3lG21Jh7/L4HE3dsMxLgHYswgcfc2vf2/SQqyBUEQBEEQBEEYtIjgEQRBEARBEARh0CKCRxAEQRAEQRCEQcuA6OERBKF/oqoqbrcbj8cT8TYulwuTyURLS0tU2/U1gzVuo9GIyWSSHp0eoKPzY7B+nvor0cYt54UgDC5E8AiC0CWcTiclJSU0NTVFtZ2qquTl5XH06NEBdTExmOO22Wzk5+djsVh6ObrBS2fnx2D+PPVHuhK3nBeCMHgQwSMIQtR4vV4OHz6M0Whk6NChWCyWiC8ivF4vjY2NJCcnD6hBloMxblVVcTqdVFRUcPjwYcaPHz+gXlt/JZLzYzB+nvoz0cQt54UgDD5E8AiCEDVOpxOv10tBQQE2my2qbb1eL06nk4SEhAF1ETFY405MTMRsNnPkyBH/ekL3iOT8GKyfp/5KtHHLeSEIg4uB89dKEIR+x0C64BHCI7/HnkHe14GN/P4EYfAgZ7MgCIIgCIIgCIMWETyCIAiCIAiCIAxaRPAIgiB0kVGjRvHII4/EZF/r1q1DURRqa2tjsj9B6GtieX4IgiB0BzEtEAQhrjjnnHOYNWtWTC7Etm7dSlJSUveDEoR+gpwfgiAMRkTwCIIgBKGqKh6PB5Op8z+PQ4YM6YWIBKH/oA9TjQQ5PwRB6C8M+pK2jQcqufjPm/j7/kH/UgWhz1BVlSanO+KvZqcnqvU7+lJVNeI4b7zxRj788EMeffRRFEVBURSee+45FEXhnXfeYfbs2VitVjZs2MDBgwe55JJLyM3NJTk5mVNPPZV169a12l/bkh1FUfjrX//KZZddhs1mY/z48fz3v//t8vv673//m6lTp2K1Whk1ahR/+MMfWj3/+OOPM378eBISEsjNzeXKK6/0P/faa68xffp0kpKSGDNmDIsWLcJut3c5FqF7hDpHYnkexOIcieT8SExM5OOPP253fsydO5f33nuv1f5ieX54PB6+/e1vM3r0aBITE5k4cSKPPvpou/WeffZZ/zmTn5/P0qVL/c/V1dVx6623kpubS0JCAtOmTeOtt96K6PhCz/PCx0e49YVtONyevg5FGIQM+gxPk9NDYVkjI5MHzkRoQRhoNLs8TLnr3T459u57F2OzRPan7NFHH2Xfvn1MmzaNe++9F4Bdu3YB8LOf/YyHHnqIMWPGkJGRwdGjR1myZAn3338/VquV559/nmuuuYY9e/YwatSosMe45557eOCBB3jwwQf505/+xHXXXceRI0fIzMyM6nVt27aNr33ta9x9991cffXVbNq0idtuu42srCxuvPFGPv30U77//e/zwgsvMH/+fKqrq/noo48AKCkp4ZprruGBBx7gkksuoaSkhB07dkQlDoXYMhDOkUjOj1GjRmEymaitrW11fvz973/n4osvZu/evYwYMSLsMbp6fni9XoYPH86rr75KVlYWmzZt4rvf/S75+fl87WtfA+CJJ55g2bJl/O53v+PCCy+krq6OjRs3+re/6qqraGpq4h//+Adjx45l9+7dGI3GiN5Doed5/IMDlNS18MWxOuaOiu7vpSB0xqAXPGajJnQ88n9eEOKetLQ0LBYLNpuNvLw8AAoLCwG49957WbhwoX/dzMxMZs6c6X9877338u9//5v//e9/3HHHHWGPceONN3LNNdcA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X76cdevWcd111wFaqcjy5cv96997772sXr2aQ4cOsX37dr7xjW9w5MgRvvOd78T2VXSCf/CoCB5BEMCfKXn22Wf9f79Au4h6/fXX2bFjB59//jnXXnttxOYEkRwzISGBG264gS+//JIPPviAO+64g29+85vk5uZy+PBhli9fzubNmzly5AirV69m//79TJ48mebmZpYuXcq6des4cuQIGzduZOvWrUyePDkmsQlCMB2dH2+88QY7d+7s9vkxbtw4XC4Xf/rTnzh06BAvvPACTz75ZKt1li9fztatW7ntttv44osvKCws5IknnqCyspKEhAR++tOf8pOf/IS///3vHDx4kI8//phnnnmmW69d6F38hgUpWlVPktWX4YnRxbOe4RmSbCUtURM89SJ4Yka1PZDd7e+CJ6qStvLycq6//npKSkpIS0tjxowZvPvuuyxcuBCA4uLiViUTNTU13HzzzZSWlpKRkcHs2bPZtGlTWJODnsJk0DM8A8uwQBCEnuG8884jMzOTvXv3cu211/qXP/zww3zrW99i/vz5ZGdn89Of/jRmPSw2m413332XO++8k7lz52Kz2bjiiit4+OGH/c8XFhby/PPPU1VVRX5+Prfffju33HILbrebqqoqrr/+esrKysjOzubyyy8Pae4iCN2ls/Nj8eLF3T4/Zs6cycMPP8zvf/97li9fzllnncWKFSu4/vrr/etMmDCB1atX8/Of/5xTTjmFxMRETj31VK655hoAfvWrX2Eymbjrrrs4ceIE+fn53Hrrrd178UKv0lbw6KYFzTHL8AT278/wiDV1zKgJmnHU2OKGtD4MphOiEjyd3TlZt25dq8d//OMf/bW/fYlkeARBCMZgMLQrnwHNKer9999vtez2228HAr0whw4dirh/R20z/2T69Ont9q+Tm5vLG2+8EfI5i8XCP//5z4iOKQjdpaPz47333vO7tBkMBv/5oRNNidsPf/hDfvjDH7Za9s1vfrPV47PPPpuNGzeGjfMXv/gFv/jFLyI+ptC/CPTY6IJHuyyNlS116wyPZlQggid2BGd4Gvp5hqf/dEr2IIEeHsnwCIIgCIIg9AfalbTF2JY6YHkdyPDUSUlbzGhV0tYigqfPMZvEtEAQhNjy4osvkpycHPKrs+HKgjDYkfNDiITgDAzgt6W2x8CW2uH2+MVNcA9PbXP0roJCaFpl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33bpmGtbunQpiqLw6KOPdum4f/e736Eoisdl2LBhXTpm3cWLF/nJT35CREQEfn5+jB49ml27drlv74rvy6SkpDrHW1EUHnroIaBrH2/hnZybmkfOS5373pVzU/sebzkvdcCxVnuwjz76SLVYLOq7776rHjp0SL3vvvvU0NBQNTs7u0Mef8WKFeqvf/1r9fPPP1cB9YsvvvC4fenSpWpISIj65Zdfqvv27VN/+MMfqv3791crKirc21x77bXq2LFj1W3btqkbN25UBw0apN51113u24uKitSYmBh13rx56sGDB9V//etfqp+fn/r222+3OO7Zs2er7733nnrw4EE1LS1NnTt3rtqvXz+1tLTUvc3999+vJiQkqGvXrlV37dqlTp06VZ02bZr7drvdro4aNUpNSUlR9+7dq65YsUKNjIxUn376afc2p0+fVv39/dVFixaphw8fVl977TXVaDSqK1eubFHcX3/9tfrNN9+ox48fV48dO6b+7//+r2o2m9WDBw922Zhr2rFjh5qUlKSOGTNGfeSRR9zXd8W4Fy9erI4cOVLNzMx0X3Jzc7t0zKqqqpcuXVITExPVe+65R92+fbt6+vRpddWqVerJkyfd23TF92VOTo7HsV6zZo0KqOvWrVNVteseb+GdnJuaT85LnffelXNT+8Ys56WOOdY9OuGZPHmy+tBDD7l/dzgcanx8vLpkyZIOj6X2ScXpdKqxsbHqiy++6L6usLBQ9fHxUf/1r3+pqqqqhw8fVgF1586d7m2+/fZbVVEU9eLFi6qqquobb7yhhoWFqVar1b3Nk08+qQ4dOrTNYs/JyVEBdf369e44zWaz+umnn7q3OXLkiAqoW7duVVVVO6EaDAY1KyvLvc2bb76pBgcHu2N94okn1JEjR3o81h133KHOnj27zWIPCwtTly1b1uVjLikpUQcPHqyuWbNGvfLKK90nla4a9+LFi9WxY8d6va2rxqyq2nvj8ssvr/f27vK+fOSRR9SBAweqTqezSx9v4Z2cm1pPzksdE7Ocm9o/Zjkvdcyx7rFD2qqqqti9ezcpKSnu6wwGAykpKWzdurUTI9OcOXOGrKwsj/hCQkKYMmWKO76tW7cSGhrKpEmT3NukpKRgMBjYvn27e5sZM2ZgsVjc28yePZtjx45RUFDQJrEWFRUBEB4eDsDu3bux2WwesQ8bNox+/fp5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",
      "text/plain": [
       "<Figure size 1000x500 with 2 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "#画线要注意的是损失是不一定在零到1之间的\n",
    "def plot_learning_curves(record_dict, sample_step=500):\n",
    "    # build DataFrame\n",
    "    train_df = pd.DataFrame(record_dict[\"train\"]).set_index(\"step\").iloc[::sample_step]\n",
    "    val_df = pd.DataFrame(record_dict[\"val\"]).set_index(\"step\")\n",
    "\n",
    "    # plot\n",
    "    fig_num = len(train_df.columns)\n",
    "    fig, axs = plt.subplots(1, fig_num, figsize=(5 * fig_num, 5))\n",
    "    for idx, item in enumerate(train_df.columns):    \n",
    "        axs[idx].plot(train_df.index, train_df[item], label=f\"train_{item}\")\n",
    "        axs[idx].plot(val_df.index, val_df[item], label=f\"val_{item}\")\n",
    "        axs[idx].grid()\n",
    "        axs[idx].legend()\n",
    "        # axs[idx].set_xticks(range(0, train_df.index[-1], 5000))\n",
    "        # axs[idx].set_xticklabels(map(lambda x: f\"{int(x/1000)}k\", range(0, train_df.index[-1], 5000)))\n",
    "        axs[idx].set_xlabel(\"step\")\n",
    "    \n",
    "    plt.show()\n",
    "\n",
    "plot_learning_curves(record, sample_step=100)  #横坐标是 steps"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 评估"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 14,
   "metadata": {
    "ExecutionIndicator": {
     "show": true
    },
    "execution": {
     "iopub.execute_input": "2025-02-03T12:05:18.654561Z",
     "iopub.status.busy": "2025-02-03T12:05:18.653783Z",
     "iopub.status.idle": "2025-02-03T12:05:20.430377Z",
     "shell.execute_reply": "2025-02-03T12:05:20.428606Z",
     "shell.execute_reply.started": "2025-02-03T12:05:18.654500Z"
    },
    "tags": []
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "loss:     0.9029\n",
      "accuracy: 0.6892\n"
     ]
    }
   ],
   "source": [
    "# dataload for evaluating\n",
    "\n",
    "# load checkpoints\n",
    "model.load_state_dict(torch.load(\"checkpoints/vgg/best.ckpt\", weights_only=True, map_location=\"cpu\"))\n",
    "\n",
    "model.eval()\n",
    "loss, acc = evaluating(model, eval_dl, loss_fct)\n",
    "print(f\"loss:     {loss:.4f}\\naccuracy: {acc:.4f}\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 推理"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 15,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "application/vnd.jupyter.widget-view+json": {
       "model_id": "e38f2f7bc3894c058cedbbae7fd3d9fe",
       "version_major": 2,
       "version_minor": 0
      },
      "text/plain": [
       "  0%|          | 0/4688 [00:00<?, ?it/s]"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "text/html": [
       "<div>\n",
       "<style scoped>\n",
       "    .dataframe tbody tr th:only-of-type {\n",
       "        vertical-align: middle;\n",
       "    }\n",
       "\n",
       "    .dataframe tbody tr th {\n",
       "        vertical-align: top;\n",
       "    }\n",
       "\n",
       "    .dataframe thead th {\n",
       "        text-align: right;\n",
       "    }\n",
       "</style>\n",
       "<table border=\"1\" class=\"dataframe\">\n",
       "  <thead>\n",
       "    <tr style=\"text-align: right;\">\n",
       "      <th></th>\n",
       "      <th>filepath</th>\n",
       "      <th>class</th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <th>0</th>\n",
       "      <td>cifar-10/test/1.png</td>\n",
       "      <td>deer</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>1</th>\n",
       "      <td>cifar-10/test/2.png</td>\n",
       "      <td>airplane</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>2</th>\n",
       "      <td>cifar-10/test/3.png</td>\n",
       "      <td>truck</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>3</th>\n",
       "      <td>cifar-10/test/4.png</td>\n",
       "      <td>ship</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>4</th>\n",
       "      <td>cifar-10/test/5.png</td>\n",
       "      <td>bird</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table>\n",
       "</div>"
      ],
      "text/plain": [
       "              filepath     class\n",
       "0  cifar-10/test/1.png      deer\n",
       "1  cifar-10/test/2.png  airplane\n",
       "2  cifar-10/test/3.png     truck\n",
       "3  cifar-10/test/4.png      ship\n",
       "4  cifar-10/test/5.png      bird"
      ]
     },
     "execution_count": 15,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# test_df\n",
    "test_ds = Cifar10Dataset(\"test\", transform=transforms_eval)\n",
    "test_dl = DataLoader(test_ds, batch_size=batch_size, shuffle=False, drop_last=False)\n",
    "\n",
    "preds_collect = []\n",
    "model.eval()\n",
    "for data, fake_label in tqdm(test_dl):\n",
    "    data = data.to(device=device)\n",
    "    logits = model(data)\n",
    "    preds = [test_ds.idx_to_label[idx] for idx in logits.argmax(axis=-1).cpu().tolist()]\n",
    "    preds_collect.extend(preds)\n",
    "    \n",
    "test_df[\"class\"] = preds_collect\n",
    "test_df.head()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 16,
   "metadata": {},
   "outputs": [],
   "source": [
    "# 导出 submission.csv\n",
    "test_df.to_csv(\"submission.csv\", index=False)"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3 (ipykernel)",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.10.14"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 4
}
